﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using xxpl1.Log;
using xxpl1.Utils;

namespace xxpl1.GAE
{
    public class DnsHelper
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(DnsHelper));
        private static readonly List<DnsItem> googleHost = new List<DnsItem>();

        public static void TestAccessbility()
        {
            googleHost.Clear();

            switch (Settings.Instance.Use)
            {
                case "GAEHTTPS":
                    TestAccessbility(Settings.Instance.Gaehttps, true);
                    break;
                case "GAEHTTP":
                    TestAccessbility(Settings.Instance.Gaehttp, false);
                    break;
                default:
                    TestAccessbility(Settings.Instance.Gaeipv6, true);
                    break;
            }
        }

        private static void TestAccessbility(List<string> ipCollection, bool https)
        {
            List<Task> tasks = new List<Task>();

            foreach (string ip in ipCollection)
            {
                IPAddress ipAddress;
                if (!IPAddress.TryParse(ip, out ipAddress))
                {
                    IPAddress[] ipAddresses = Dns.GetHostAddresses(ip);
                    tasks.AddRange(ipAddresses.Select(address => new DnsItem(address, https)).Select(item => Task.Factory.StartNew(TestAccessbilityAction, item)));
                }
                else
                {
                    DnsItem item = new DnsItem(ipAddress, https);
                    tasks.Add(Task.Factory.StartNew(TestAccessbilityAction, item));
                }
            }

            Task.Factory.ContinueWhenAll(tasks.ToArray(), Sort);
        }

        private static void Sort(object state)
        {
            googleHost.Sort((a, b) => a.Delay.CompareTo(b.Delay));
            logger.DebugFormat("Google Host sorted, Fast={0} {1}ms", googleHost[0].Address, googleHost[0].Delay);
        }

        private static void TestAccessbilityAction(object parameter)
        {
            HttpWebResponse response = null;
            HttpWebRequest requestToProxy = null;
            DnsItem dnsItem = (DnsItem)parameter;
            try
            {

                string url = BuildUrl(dnsItem);
                requestToProxy = (HttpWebRequest)WebRequest.Create(url);
                requestToProxy.Host = Settings.Instance.Gaeapp[0];
                requestToProxy.Proxy = EmptyProxy.Instance;
                requestToProxy.Timeout = 5000;
                requestToProxy.AllowAutoRedirect = false;

                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();

                response = (HttpWebResponse)requestToProxy.GetResponse();

                stopWatch.Stop();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    dnsItem.Delay = stopWatch.ElapsedMilliseconds;
                    googleHost.Add(dnsItem);
                    logger.DebugFormat("{0} - OK, https = {1}", dnsItem.Address, dnsItem.Https);
                }
                else
                {
                    logger.DebugFormat("{0} - unavailable, https={1}", dnsItem.Address, dnsItem.Https);
                }
            }
            catch (Exception e)
            {
                logger.DebugFormat("{0} - unavailable, https={1}", dnsItem.Address, dnsItem.Https);
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
                if (requestToProxy != null)
                {
                    requestToProxy.Abort();
                }
            }
        }

        private static string BuildUrl(DnsItem dnsItem)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(dnsItem.Https ? "https://" : "http://");
            sb.Append(dnsItem.Address.AddressFamily == AddressFamily.InterNetworkV6 ? "[" + dnsItem.Address + "]" : dnsItem.Address.ToString());
            sb.Append("/xpp3");
            return sb.ToString();
        }

        public static string GetHost(string host)
        {
            int index = Math.Abs(host.GetHashCode() % Settings.Instance.Gaeapp.Count);
            return googleHost[Math.Min(index, googleHost.Count - 1)].Address.ToString();
        }

        public static string GetGaeHost(string url)
        {
            return Settings.Instance.Gaeapp[Math.Abs(url.GetHashCode() % Settings.Instance.Gaeapp.Count)];
        }
    }
}
